#include "UMLLine.h"
#include "DiagramVisitor.h"

#include <algorithm>
#include <QDebug>

UMLLine::UMLLine(const QPoint& p1, const QPoint& p2, const QString& text)
:	UMLObject(text)
{
	_vertices.push_back(p1);
	_vertices.push_back(p2);

	updateSize();
}

UMLLine::UMLLine(const std::vector<QPoint>& vertices, const BUMLSText& text)
:	UMLObject(text),
	_vertices(vertices)
{
	updateSize();
}

UMLLine& UMLLine::operator=(const UMLObject& obj)
{
	const UMLLine& line = *static_cast<const UMLLine*>(&obj);

	_vertices = line._vertices;
	_rectCache = line._rectCache;

	UMLObject::operator=(obj);
	// notifyObservers() called above
	
	return *this;
}

UMLLine* UMLLine::clone() const
{
	return new UMLLine(*this);
}

void UMLLine::accept(DiagramVisitor& dv)
{
	dv.visit(*this);
}

QPoint UMLLine::position() const
{
	return _vertices[0];
}

void UMLLine::setPosition(const QPoint& pt)
{
	// Note that we're moving the first vertex.
	
	QPoint delta = pt - _vertices.front();

	// Gotta translate all the vertices
	for (iterator iter = _vertices.begin(); iter != _vertices.end(); ++iter)
		*iter += delta;

	// just move the rect, don't do the whole update
	_rectCache.translate(delta);

	notifyObservers();
}

QRect UMLLine::rect() const
{
	return _rectCache;
}

void UMLLine::setEnd(const QPoint& pt)
{
	_vertices.back() = pt;
	updateSize();

	notifyObservers();
}

UMLLine::size_type UMLLine::vertexCount() const
{
	return _vertices.size();
}

QPoint UMLLine::vertex(size_type index) const
{
	return _vertices[index];
}

UMLLine::size_type UMLLine::vertex(const QPoint& pt) const
{
	 const_iterator match = find(_vertices.begin(), _vertices.end(), pt);

	 return (match != _vertices.end()) ?
		 (match - _vertices.begin()) : NO_VERTEX;
}

void UMLLine::setVertex(int index, const QPoint& pt)
{
	_vertices[index] = pt;

	if (!_rectCache.contains(pt))
		updateSize();

	notifyObservers();
}

bool pointOnLine(const QPoint& p1, const QPoint& p2, const QPoint& pt)
{
	if (!QRect(p1, p2).contains(pt))
		return false;

	// TODO improve this algorithm?
	
	int dy = p2.y() - p1.y();
	int dx = p2.x() - p1.x();

	return (pt.x() - p1.x()) / dx == (pt.y() - p1.y()) / dy;
}

UMLLine::size_type UMLLine::segment(const QPoint& pt) const
{
	// boo, can't really use an STL algorithm here
	
	QPoint first = _vertices.front();
	for (const_iterator iter = ++_vertices.begin(); iter != _vertices.end();
			++iter)
	{
		QPoint second = *iter;

		if (pointOnLine(first, second, pt))
			return iter - _vertices.begin() - 1;

		first = second;
	}

	return NO_VERTEX;
}

void UMLLine::updateSize()
{
	const_iterator iter = _vertices.begin();

	QPoint least = *iter;
	QPoint greatest = *iter;

	for (; iter != _vertices.end(); ++iter)
	{
		int x = iter->x();

		if (x < least.x())
			least.setX(x);
		else if (x > greatest.x())
			greatest.setX(x);

		int y = iter->y();

		if (y < least.y())
			least.setY(y);
		else if (y > greatest.y())
			greatest.setY(y);
	}

	qDebug() << "Greatest is " << greatest << ", least is " << least;

	_rectCache.setTopLeft(least);
	_rectCache.setBottomRight(greatest);
}

